//    This file is part of PZong.
//
//    PZong is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    PZong is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with Modabu; if not, write to the Free Software
//    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

//    Copyright 2007, Priyesh Dixit, Kunal Dixit

// Includes
#include <PA9.h>       // Include for PA_Lib

// PAGfx Includes
#include "gfx/all_gfx.c"
#include "gfx/all_gfx.h"
// Audio includes
#include "wallbouncesound.h"
#include "paddlebouncesound.h"
#include "missballsound.h"
#include "youwinsound.h"
#include "gameoversound.h"
#include "music.h"

enum GAME_STATE {MYSPLASH, MAIN_MENU, GAME, PAUSE, GAME_OVER, YOU_WIN};

//Screen size: 255,191~
//Play sound:
//PA_PlaySimpleSound(0, saberoffsound);
//PA_PlaySoundRepeat(0, saberoffsound);

//Load sprite from PAgfx:
//PA_LoadSpritePal(0, // Bottom Screen
//					0, // Palette number
//					(void*)sprite0_Pal);	// Palette name
//PA_CreateSprite(0, 0,(void*)paddle_Sprite, OBJ_SIZE_64X64,1, 0, 50, 50);
//screenID, objID, data, shape, size, color mode, palette, x,y

//Waiting.. 60 frames ~= 1 second

//coordinates: down = +1, right= +1

void initMySplash()
{
	PA_LoadSpritePal(1, // Screen
					0, // Palette number
					(void*)prilogo_Pal);	// Palette name
					
	PA_CreateSprite(1, 0,(void*)prilogo_Sprite, OBJ_SIZE_64X64,1, 0, 90, 70);
	PA_OutputText(0, 5,5, "Game by Priyesh Dixit");
	PA_OutputText(0, 8,6, "using PALib");
	
	PA_OutputText(0, 5,8, "Graphics by Ku");
	PA_OutputText(0, 8,9, "http://kustudios.net");
	
}

void destroyMySplash()
{
	PA_DeleteSprite(1,0);
	PA_ClearTextBg(0);
}

void initMainMenu()
{
	PA_EasyBgLoad(0, 3, menubottomBG);
	PA_EasyBgLoad(1, 3, menutopBG);
}

void destroyMainMenu()
{
	PA_DeleteBg(0, 3);
	PA_DeleteBg(1, 3);
}

void initGameScreen()
{
	PA_LoadSpritePal(0, // Screen
					0, // Palette number
					(void*)paddle_Pal);	// Palette name
					
	PA_CreateSprite(0, 0,(void*)paddle_Sprite, OBJ_SIZE_64X64,1, 0, 100, 165);
	//screenID, objID, data, shape, size, color mode, palette, x,y
	
	PA_DualLoadSpritePal(
					1, // Palette number
					(void*)ball_Pal);	// Palette name
					
	PA_DualCreateSprite(1,(void*)ball_Sprite, OBJ_SIZE_32X32,1, 1, 124, 83);
	
	PA_EasyBgLoad(0, 3, gamebottomBG);
	PA_EasyBgLoad(1, 3, gametopBG);	
}

void destroyGameScreen()
{
	PA_DeleteSprite(0, 0);
	PA_DualDeleteSprite(1);
	
	PA_DeleteBg(0, 3);
	PA_DeleteBg(1, 3);
}

void initGameOver()
{
	PA_EasyBgLoad(0, 3, gameoverbottomBG);
	PA_EasyBgLoad(1, 3, gameovertopBG);
}

void destroyGameOver()
{
	PA_DeleteBg(0, 3);
	PA_DeleteBg(1, 3);
}

void initYouWin()
{
	PA_EasyBgLoad(0, 3, winbottomBG);
	PA_EasyBgLoad(1, 3, wintopBG);
}

void destroyYouWin()
{
	PA_DeleteBg(0, 3);
	PA_DeleteBg(1, 3);
}

//Splash screen wait constant
const s16 SPLASH_WAIT_FRAMES = 240;
const s16 NEXTLEVEL_WAIT_FRAMES = 120;
const s8 MAX_LIVES = 5;

struct GameInfoType
{
	s8 goal;
	s8 score;
	s8 level;
	s8 lives;
	
	u8 rotset;
	
	s16 ballRotateAngle;
	s32 ballX;
	s32 ballY;
	
	s8 currentBallSpeed;
	s8 ballVelocityX;
	s8 ballVelocityY;
	
	s32 paddleX;
	s32 paddleY;
};

void StopChannelIfBusy(u8 channel)
{
	if(PA_SoundChannelIsBusy(channel))
	{
		PA_StopSound(channel);
	}
}

void initializeGame(GameInfoType* gameInfo)
{
	//initialize game
	gameInfo->score= 0;
	gameInfo->goal = gameInfo->level * 3;
	
	gameInfo->ballRotateAngle=0;
	PA_DualSetRotset(gameInfo->rotset,0, 256, 256);
	
	gameInfo->ballX=124;
	gameInfo->ballY=83;
	
	gameInfo->currentBallSpeed = gameInfo->level + 1;
	
	gameInfo->ballVelocityX = 0;
	gameInfo->ballVelocityY = gameInfo->currentBallSpeed;
	
	gameInfo->paddleX=100;
	gameInfo->paddleY=165;
}
void checkWallBounds(GameInfoType* gameInfo);
void checkPaddleBounce(GameInfoType* gameInfo);

int main()
{
	GAME_STATE gameState;
	GameInfoType gameInfo;
	gameState = MYSPLASH;
	
	PA_Init();    // Initializes PA_Lib
	PA_InitVBL(); // Initializes a standard VBL
	PA_InitText(0,0);	
	PA_InitText(1,0);
	
	PA_InitSound();
	
	PA_SetScreenSpace(0);
	
	//background
	//PA_PlaySoundEx2(1, missballsound, sound_size(missballsound), 64, 11025, );
	
	// Infinite loop to keep the program running
	while (1)
	{
		PA_ClearTextBg(0);
		PA_ClearTextBg(1);
		
		if (gameState == MYSPLASH)
		{
			//show my splash
			initMySplash();
			s16 time = SPLASH_WAIT_FRAMES;
			while (time)
			{
				time--;		
				PA_WaitForVBL();
			}
			destroyMySplash();
			gameState = MAIN_MENU;
		}
		else if (gameState == MAIN_MENU)
		{
			initMainMenu();
			//PA_PlaySimpleSound(3, welcomesound);
			while(!Pad.Newpress.Start && !Stylus.Newpress)
			{
				PA_WaitForVBL();
			}
			destroyMainMenu();
			
			gameInfo.level=1;
			gameInfo.lives=MAX_LIVES;
			
			initializeGame(&gameInfo);
			initGameScreen();
			gameState = GAME;
		}
		else if (gameState == GAME_OVER)
		{
			destroyGameScreen();
			initGameOver();
			StopChannelIfBusy(3);
			PA_PlaySimpleSound(3, gameoversound);
			while(!Pad.Newpress.Start)
			{
				PA_WaitForVBL();
			}
			destroyGameOver();
			
			gameInfo.level=1;
			gameInfo.lives=MAX_LIVES;
			
			initializeGame(&gameInfo);
			initGameScreen();
			gameState = GAME;
		}
		//goal reached.. now in YOUWIN state
		else if (gameState == YOU_WIN)
		{
			destroyGameScreen();
			initYouWin();
			StopChannelIfBusy(3);
			PA_PlaySimpleSound(3, youwinsound);
			s16 time = NEXTLEVEL_WAIT_FRAMES;
			while(time)
			{
				time--;
				PA_WaitForVBL();
			}
			destroyYouWin();
			
			gameInfo.level++;
			initializeGame(&gameInfo);
			initGameScreen();
			gameState = GAME;
		}
		else if (gameState == GAME || gameState == PAUSE)
		{
			//draw game HUD
			PA_OutputText(1,0,1,"Level: %d \nGoal: %d",gameInfo.level,gameInfo.goal);
			PA_OutputText(1,20,1,"Score: %d \nLives: %d",gameInfo.score, gameInfo.lives);
			
			//if paused display "Paused"
			if (gameState == PAUSE)
			{
				PA_SetTextCol(0, 0, 0, 0);
				PA_OutputText(0, 10, 6, "Paused");
				//check for unpause
				if (Pad.Newpress.Start)
				{
					PA_SetTextCol(0, 31, 31, 31);
					gameState = GAME;
				}
			}
			else//only do the rest if NOT paused
			{
				//background music
				if (PA_SoundChannelIsBusy(4) == false)
				{
					PA_PlaySimpleSound(4, music);
				}
				
				//controlling the paddle...
				if (Pad.Held.Left == true)
				{
					gameInfo.paddleX-=2;
				}
				else if (Pad.Held.Right == true)
				{
					gameInfo.paddleX+=2;
				}
				else if (Stylus.Held)
				{
					gameInfo.paddleX = Stylus.X-32;
				}
				PA_SetSpriteXY(0,0, gameInfo.paddleX, gameInfo.paddleY);
		
				
				//got past paddle!
				if (gameInfo.ballY > gameInfo.paddleY+190)
				{
					StopChannelIfBusy(2);
					PA_PlaySimpleSound(2, missballsound);
					gameInfo.lives--;
					if (gameInfo.lives >= 0)
					{
						gameInfo.ballX=124;
						gameInfo.ballY=83;
						
						gameInfo.ballVelocityX = 0;
						gameInfo.ballVelocityY = gameInfo.currentBallSpeed;
					}
					else
					{
						gameState = GAME_OVER;
					}
				}
				checkPaddleBounce(&gameInfo);
				checkWallBounds(&gameInfo);
				
				//check microphone for slowing down ball
				u8 volume = PA_MicGetVol();
				float volRatio = 1.0f;
				
				if  (volume > 99)
				{
					volRatio = (1.0f - ((volume * 1.0f)/300.0f));
				}
				if (volRatio > 1)
				{
					volRatio = 1;
				}
				//u16 ratio = int(100 * volRatio);
				
				//move ball
				gameInfo.ballX += int(gameInfo.ballVelocityX * volRatio);
				gameInfo.ballY += int(gameInfo.ballVelocityY * volRatio);
				gameInfo.ballRotateAngle++;
				PA_DualSetRotset(gameInfo.rotset,gameInfo.ballRotateAngle, 256, 256);
				PA_DualSetSpriteRotEnable(1, gameInfo.rotset);
				PA_DualSetSpriteXY(1, gameInfo.ballX, gameInfo.ballY);
				
				if (gameInfo.score >= gameInfo.goal)
				{
					gameState = YOU_WIN;
				}
				//check for pause
				if (Pad.Newpress.Start)
				{
					gameState = PAUSE;
					StopChannelIfBusy(0);
					StopChannelIfBusy(1);
					StopChannelIfBusy(2);
					StopChannelIfBusy(3);
					StopChannelIfBusy(4);
				}
			}
		}
		
		PA_WaitForVBL();
	}
	destroyGameScreen();
	return 0;
}

//checks out of bounds
void checkWallBounds(GameInfoType* gameInfo)
{
	//hit left wall
	if (gameInfo->ballX < 0)
	{
		gameInfo->ballVelocityX = gameInfo->currentBallSpeed;
		StopChannelIfBusy(0);
		PA_PlaySimpleSound(0, wallbouncesound);
	}
	//hit right wall
	if (gameInfo->ballX > 223)
	{
		gameInfo->ballVelocityX = -gameInfo->currentBallSpeed;
		StopChannelIfBusy(0);
		PA_PlaySimpleSound(0, wallbouncesound);
	}
	//hit ceiling
	if (gameInfo->ballY < 32)
	{
		gameInfo->ballVelocityY = gameInfo->currentBallSpeed;
		StopChannelIfBusy(0);
		PA_PlaySimpleSound(0, wallbouncesound);
	}
}

//checks for paddle collision
void checkPaddleBounce(GameInfoType* gameInfo)
{
	if (gameInfo->ballX+32 > gameInfo->paddleX && gameInfo->ballX < gameInfo->paddleX+64)
	{
		if (gameInfo->ballY+32 >= gameInfo->paddleY+190 && gameInfo->ballY < gameInfo->paddleY+174)
		{
			//collision
			gameInfo->ballVelocityY = -gameInfo->currentBallSpeed;
			//check which half of paddle was hit
			int diff = gameInfo->paddleX - gameInfo->ballX;
			if (diff < 16)
			{
				//left side
				gameInfo->ballVelocityX = gameInfo->currentBallSpeed;
			}
			else if (diff > 48)
			{
				//right side
				gameInfo->ballVelocityX = -gameInfo->currentBallSpeed;
			}
			StopChannelIfBusy(1);
			PA_PlaySimpleSound(1, paddlebouncesound);
			gameInfo->score++;
		}
	}
}

